package main

import (
	"encoding/json"
	"fabric-orderer-tm/consensus"
	"flag"
	"fmt"
	"os"
	"runtime/debug"
	"time"

	"github.com/tendermint/tendermint/types/priv_validator"

	"github.com/op/go-logging"
	"github.com/tendermint/tendermint/types"
)

var logger = logging.MustGetLogger("aramaki")

const (
	chainID string = "chain-Chari"
)

func main() {
	defer func() {
		if err := recover(); err != nil {
			logger.Error(err)
			logger.Error(string(debug.Stack()))
		}
	}()

	format := logging.MustStringFormatter(`[%{module}] %{time:2006-01-02 15:04:05} [%{level}] [%{longpkg} %{shortfile}] { %{message} }`)

	backendConsole := logging.NewLogBackend(os.Stderr, "", 0)
	backendConsole2Formatter := logging.NewBackendFormatter(backendConsole, format)

	logging.SetBackend(backendConsole2Formatter)
	logging.SetLevel(logging.INFO, "")

	var size int
	flag.IntVar(&size, "s", 4, "default orderers size")
	flag.Parse()

	if size < 4 {
		logger.Error("orderers size must be bigger than 4 to meet PBFT")
		return
	}

	//orderers := make([]*types.PrivValidatorFS, size)
	orderers := make([]*privval.FilePV, size)
	validators := make([]types.GenesisValidator, size)

	for k := 0; k < size; k++ {
		//orderers[k] = types.GenPrivValidatorFS("")
		orderers[k] = privval.GenFilePV("")
		validators[k] = types.GenesisValidator{
			PubKey: orderers[k].PubKey,
			Power:  1,
			Name:   fmt.Sprintf("orderer%d", k),
		}
	}

	genesisDoc := &types.GenesisDoc{
		GenesisTime: time.Now(),
		ChainID:     chainID,
		Validators:  validators,
		AppHash:     []byte(consensus.AppHash),
	}

	genesisRaw, err := json.MarshalIndent(genesisDoc, "", " ")
	if err != nil {
		logger.Error(err)
		return
	}

	if err = os.MkdirAll("channel-artifacts", 0766); err != nil {
		logger.Error(err)
		return
	}
	if err = writeToDisk("channel-artifacts/chari.genesis.json", genesisRaw); err != nil {
		logger.Error(err)
		return
	}

	for k, orderer := range orderers {
		raw, err := json.MarshalIndent(orderer, "", " ")
		if err != nil {
			logger.Error(err)
			return
		}

		if err = os.MkdirAll(fmt.Sprint("crypto-config/chari", k), 0766); err != nil {
			logger.Error(err)
			return
		}

		if err = writeToDisk(fmt.Sprint("crypto-config/chari", k, "/priv_validator.json"), raw); err != nil {
			logger.Error(err)
			return
		}
	}
}

func writeToDisk(fileName string, raw []byte) error {
	file, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		return err
	}
	defer file.Close()

	if _, err = file.Write(raw); err != nil {
		return err
	}
	if err = file.Sync(); err != nil {
		return err
	}
	return nil
}

/* // PrivValidatorFS implements PrivValidator using data persisted to disk
// to prevent double signing. The Signer itself can be mutated to use
// something besides the default, for instance a hardware signer.
type PrivValidatorFS struct {
	Address       data.Bytes       `json:"address"`
	PubKey        crypto.PubKey    `json:"pub_key"`
	LastHeight    int              `json:"last_height"`
	LastRound     int              `json:"last_round"`
	LastStep      int8             `json:"last_step"`
	LastSignature crypto.Signature `json:"last_signature,omitempty"` // so we dont lose signatures
	LastSignBytes data.Bytes       `json:"last_signbytes,omitempty"` // so we dont lose signatures
	// PrivKey should be empty if a Signer other than the default is being used.
	PrivKey crypto.PrivKey `json:"priv_key"`
	Signer  `json:"-"`

	// For persistence.
	// Overloaded for testing.
	filePath string
	mtx      sync.Mutex
}

// GenPrivValidatorFS generates a new validator with randomly generated private key
// and sets the filePath, but does not call Save().
func GenPrivValidatorFS(filePath string) *PrivValidatorFS {
	privKey := crypto.GenPrivKeyEd25519().Wrap()
	return &PrivValidatorFS{
		Address:  privKey.PubKey().Address(),
		PubKey:   privKey.PubKey(),
		PrivKey:  privKey,
		LastStep: stepNone,
		Signer:   NewDefaultSigner(privKey),
		filePath: filePath,
	}
} */
